using System;
using System.Collections.Generic;

namespace QFramework.Framework.Util
{
    /// <summary>
    ///     对象池接口
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IPool<T>
    {
        T Allocate();

        bool Recycle(T obj);
    }

    /// <summary>
    ///     对象工厂接口
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IObjectFactory<T>
    {
        T Create();
    }

    /// <summary>
    ///     对象池基类：工厂模式
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Pool<T> : IPool<T>
    {
        protected readonly Stack<T> CacheStack = new();
        protected IObjectFactory<T> Factory;

        /// <summary>
        ///     default is 5
        /// </summary>
        protected int MaxCount = 5;

        /// <summary>
        ///     Gets the current count.
        /// </summary>
        public int CurCount => CacheStack.Count;

        public virtual T Allocate()
        {
            return CacheStack.Count == 0 ? Factory.Create() : CacheStack.Pop();
        }

        public abstract bool Recycle(T obj);
    }

    /// <summary>
    ///     自定义对象工厂
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CustomObjectFactory<T> : IObjectFactory<T>
    {
        protected Func<T> FactoryMethod;

        public CustomObjectFactory(Func<T> factoryMethod)
        {
            FactoryMethod = factoryMethod;
        }

        public T Create()
        {
            return FactoryMethod();
        }
    }

    /// <summary>
    ///     简易对象池：unsafe but fast
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SimpleObjectPool<T> : Pool<T>
    {
        private readonly Action<T> _resetMethod;

        public SimpleObjectPool(Func<T> factoryMethod, Action<T> resetMethod = null, int initCount = 0)
        {
            Factory = new CustomObjectFactory<T>(factoryMethod);
            _resetMethod = resetMethod;

            for (var i = 0; i < initCount; i++) CacheStack.Push(Factory.Create());
        }

        public override bool Recycle(T obj)
        {
            if (_resetMethod != null) _resetMethod(obj);

            CacheStack.Push(obj);
            return true;
        }
    }
}